Skip to content

fix(blob): normalize contentDisposition in copy() when addRandomSuffix is enabled#1045

Open
matingathani wants to merge 1 commit intovercel:mainfrom
matingathani:fix/blob-copy-content-disposition
Open

fix(blob): normalize contentDisposition in copy() when addRandomSuffix is enabled#1045
matingathani wants to merge 1 commit intovercel:mainfrom
matingathani:fix/blob-copy-content-disposition

Conversation

@matingathani
Copy link
Copy Markdown
Contributor

Summary

The copy() function has the same contentDisposition inconsistency as put() (issue #903): when addRandomSuffix: true is set, the Vercel Blob API returns a suffixed filename in the Content-Disposition header (e.g. attachment; filename="report-abc123.pdf"), but the caller expects the original toPathname filename.

This PR:

  • Extracts normalizeContentDisposition from put.ts into put-helpers.ts as a shared utility
  • Applies it in copy.ts to normalize the returned contentDisposition
  • Adds two regression tests for the new behaviour

Before

const result = await copy('source.pdf', 'reports/annual.pdf', {
  access: 'public',
  addRandomSuffix: true,
});
// result.contentDisposition === 'attachment; filename="annual-abc123.pdf"' ❌

After

const result = await copy('source.pdf', 'reports/annual.pdf', {
  access: 'public',
  addRandomSuffix: true,
});
// result.contentDisposition === 'attachment; filename="annual.pdf"' ✅
// result.pathname             === 'reports/annual-abc123.pdf' (unchanged)

Related: #903 (same fix applied to put() in #1041)

…x is enabled

When copy() is called with addRandomSuffix: true, the Vercel Blob API
returns a contentDisposition header containing the suffixed filename
(e.g. 'attachment; filename="report-abc123.pdf"'). The SDK now normalizes
this to always use the original toPathname filename by moving the shared
normalizeContentDisposition helper to put-helpers.ts and applying it in
copy.ts, matching the behaviour already enforced for put().

Adds two regression tests: one confirming normalization fires on suffix
mismatch, one confirming no-op when pathname is unchanged.
Copilot AI review requested due to automatic review settings April 3, 2026 01:51
@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Apr 3, 2026

🦋 Changeset detected

Latest commit: 38491bb

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 2 packages
Name Type
@vercel/blob Patch
vercel-storage-integration-test-suite Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel
Copy link
Copy Markdown
Contributor

vercel bot commented Apr 3, 2026

@matingathani is attempting to deploy a commit to the Curated Tests - Permanent E2E Team on Vercel.

A member of the Team first needs to authorize it.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes an inconsistency in copy() where the API may return a suffixed filename in contentDisposition when addRandomSuffix: true, even though callers expect the original toPathname filename.

Changes:

  • Added a shared normalizeContentDisposition() utility to rewrite the returned Content-Disposition filename back to the originally requested filename.
  • Applied normalization in copy() responses.
  • Added node regression tests and a patch changeset entry.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
packages/blob/src/put-helpers.ts Introduces normalizeContentDisposition() helper for adjusting API-returned contentDisposition.
packages/blob/src/copy.ts Normalizes contentDisposition using toPathname vs API-returned pathname.
packages/blob/src/index.node.test.ts Adds regression tests for copy() contentDisposition normalization behavior.
.changeset/fix-blob-copy-content-disposition.md Documents the patch release for the copy() behavior change.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +13 to +15
* instead of the API-returned pathname (which may include a random suffix).
* This ensures `contentDisposition` always reflects the name the caller
* provided, regardless of `addRandomSuffix` being enabled.
Copy link

Copilot AI Apr 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The docstring claims this "ensures contentDisposition always reflects the name the caller provided", but the implementation only rewrites when the header contains an exact quoted match of the response filename (and otherwise returns the original string). Consider tightening the wording to reflect the actual conditional behavior, or expanding the normalization to cover all expected Content-Disposition formats if the stronger guarantee is required.

Suggested change
* instead of the API-returned pathname (which may include a random suffix).
* This ensures `contentDisposition` always reflects the name the caller
* provided, regardless of `addRandomSuffix` being enabled.
* instead of the API-returned pathname (which may include a random suffix),
* when the header contains an exact quoted match for the response filename.
* If no such quoted filename is present, the original contentDisposition
* value is returned unchanged.

Copilot uses AI. Check for mistakes.
Comment on lines +7 to +11
When `copy()` is called with `addRandomSuffix: true`, the Vercel Blob API
returns a `contentDisposition` header containing the suffixed filename
(e.g. `attachment; filename="report-abc123.pdf"`). The SDK now normalizes
this to always use the original `toPathname` filename, matching the
behaviour already enforced for `put()`.
Copy link

Copilot AI Apr 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This changeset says the copy() normalization "matches the behaviour already enforced for put()", but put() currently returns response.contentDisposition unchanged (see packages/blob/src/put.ts). Either update the changeset wording to avoid claiming parity with put(), or update put() to use the same normalization helper so the statement is accurate.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants